package edu.dhbw.andar.sample;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;
import java.util.concurrent.ExecutionException;

import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLUtils;
import android.util.Log;
import edu.dhbw.andar.ARObject;
import edu.dhbw.andar.util.Group;
import edu.dhbw.andar.util.Material;
import edu.dhbw.andar.util.Model;

public class Arbject extends ARObject {
	public boolean isVisible = true;
	
	private Model model;
	private String name;
	private String patternID;
	private String nfcID;
	private String value;
	private double markerWidth;
	private double[] center;
	
	private Group[] texturedGroups;
	private Group[] nonTexturedGroups;
	private HashMap<Material, Integer> textureIDs = new HashMap<Material, Integer>();
	
	public Arbject(Model model, String name, String patternID, String nfcID) {
		super(name, patternID, 80, new double[]{0,0});
		
		String card;
		try {
			card = getSharedData(nfcID);
			
			Log.d("Arbject", "card = " + card + ", value = " + value);
			
			if(card.equals("none")){
				changeModel(model, "none");
			}
			if(!card.equals(value)){
				changeModel(CustomActivity.getModel(card), card);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		
		this.name = name;
		this.patternID = patternID;
		this.markerWidth = 80;
		this.center = new double[]{0,0};
		
		this.nfcID = nfcID;
		
		isVisible = true;
	}
	
	public Arbject(String modelName, String patternID, String nfcID) {
		super(modelName, patternID, 80, new double[]{0,0});
		
		changeModel(model, "none");
		this.name = modelName;
		this.patternID = patternID;
		this.nfcID = nfcID;
		this.markerWidth = 80;
		this.center = new double[]{0,0};
		
		isVisible = false;
	}
	
    /**
     * Get the value for a shared variable on the server.
     * @param key The variable that you want the value for.
     * @return The value for the parameter {@code key}, represented as a {@link java.lang.String}.
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public String getSharedData(String key) throws InterruptedException, ExecutionException{
    	return new Client().execute(key).get();
    }
	
	public void changeModel(Model newModel, String value){
		model = newModel;
		model.finalize();
		
		Log.d("ChangeModel", "Setting model to " + value);
		this.value = value;
		
		isVisible = true;
		
		markForReinitialization();
	}
	
	public void setModelToNone(){
		value = "none";
		isVisible = false;
	}

	@Override
	public void init(GL10 gl) {
		// separate texture from non textured groups for performance reasons
		Vector<Group> groups = model.getGroups();
		Vector<Group> texturedGroups = new Vector<Group>();
		Vector<Group> nonTexturedGroups = new Vector<Group>();
		for (Iterator<Group> iterator = groups.iterator(); iterator.hasNext();) {
			Group currGroup = iterator.next();
			if (currGroup.isTextured()) {
				texturedGroups.add(currGroup);
			} else {
				nonTexturedGroups.add(currGroup);
			}
		}
		this.texturedGroups = texturedGroups.toArray(new Group[texturedGroups.size()]);
		this.nonTexturedGroups = nonTexturedGroups.toArray(new Group[nonTexturedGroups.size()]);
		
		int[]  tmpTextureID = new int[1];
		//load textures of every material(that has a texture):
		Iterator<Material> materialI = model.getMaterials().values().iterator();
		while (materialI.hasNext()) {
			Material material = (Material) materialI.next();
			if(material.hasTexture()) {
				//load texture
				gl.glGenTextures(1, tmpTextureID, 0);
				gl.glBindTexture(GL10.GL_TEXTURE_2D, tmpTextureID[0]);
				textureIDs.put(material, tmpTextureID[0]);
				GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, material.getTexture(),0);
				material.getTexture().recycle();
				gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
				gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); 
			}
		}
		//transfer vertices to video memory
	}

	@Override
	public synchronized void draw(GL10 gl) {
		super.draw(gl);
		
		//Only do the cool update stuff if we are not changing the 3D model.
		if(isVisible){
			//do positioning:
			gl.glScalef(model.scale, model.scale, model.scale);
			gl.glTranslatef(model.xpos, model.ypos, model.zpos);
			gl.glRotatef(model.xrot, 1, 0, 0);
			gl.glRotatef(model.yrot, 0, 1, 0);
			gl.glRotatef(model.zrot, 0, 0, 1);
			
			gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
			
			//first draw non textured groups
			gl.glDisable(GL10.GL_TEXTURE_2D);
			int cnt = nonTexturedGroups.length;
			for (int i = 0; i < cnt; i++) {
				Group group = nonTexturedGroups[i];
				Material mat = group.getMaterial();
				if(mat != null) {
					gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, mat.specularlight);
					gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, mat.ambientlight);
					gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, mat.diffuselight);
					gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, mat.shininess);
				}
				gl.glVertexPointer(3,GL10.GL_FLOAT, 0, group.vertices);
		        gl.glNormalPointer(GL10.GL_FLOAT,0, group.normals);	        
		        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, group.vertexCount);
			}
			
			//now we can continue with textured ones
			gl.glEnable(GL10.GL_TEXTURE_2D);
			gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
			
			cnt = texturedGroups.length;
			for (int i = 0; i < cnt; i++) {
				Group group = texturedGroups[i];
				Material mat = group.getMaterial();
				if(mat != null) {
					gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, mat.specularlight);
					gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, mat.ambientlight);
					gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, mat.diffuselight);
					gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, mat.shininess);
					if(mat.hasTexture()) {
						gl.glTexCoordPointer(2,GL10.GL_FLOAT, 0, group.texcoords);
						gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs.get(mat).intValue());
					}
				}
				gl.glVertexPointer(3,GL10.GL_FLOAT, 0, group.vertices);
		        gl.glNormalPointer(GL10.GL_FLOAT,0, group.normals);	        
		        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, group.vertexCount);
			}
			
			gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
			gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
		}
	}
	
	public void setScale(float scale){
		model.setScale(scale);
	}
	
	public Model getModel(){
		return model;
	}
	
	public String getName(){
		return name;
	}
	
	public String getPatternID(){
		return patternID;
	}
	
	public String getID(){
		return nfcID;
	}
	
	public double getMarkerWidth(){
		return markerWidth;
	}
	
	public double[] getCenter(){
		return center;
	}
	
	public String getValue(){
		return value;
	}
	
	public float getX(){
		return model.xpos;
	}
	
	public float getY(){
		return model.ypos;
	}
}
